package com.quectel.core.module.partyeducationexam.service.impl;


import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.base.Preconditions;
import com.quectel.constant.core.partyeducationexam.PartyEducationExamConstants;
import com.quectel.constant.core.partyeducationexam.PartyEducationExamPersonConstants;
import com.quectel.constant.core.partyeducationexam.PartyEducationQuestionConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.util.aspect.lock.annotation.Lock;
import com.quectel.core.constants.RedisCacheConstants;
import com.quectel.core.constants.RedisLockConstants;
import com.quectel.core.module.partybuilding.dto.PartyOrganizationDto;
import com.quectel.core.module.partybuilding.service.PartyOrganizationService;
import com.quectel.core.module.partyeducationexam.dao.PartyEducationTrainingExamDao;
import com.quectel.core.module.partyeducationexam.dto.*;
import com.quectel.core.module.partyeducationexam.dto.ref.QuestionRefExamDto;
import com.quectel.core.module.partyeducationexam.entity.PartyEducationTrainingExamEntity;
import com.quectel.core.module.partyeducationexam.service.PartyEducationTrainingExamPersonHistoryService;
import com.quectel.core.module.partyeducationexam.service.PartyEducationTrainingExamPersonService;
import com.quectel.core.module.partyeducationexam.service.PartyEducationTrainingExamQuestionService;
import com.quectel.core.module.partyeducationexam.service.PartyEducationTrainingExamService;
import com.quectel.core.module.partyeducationtraining.dto.TrainingDto;
import com.quectel.core.module.partyeducationtraining.service.TrainingService;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.kit.CacheKit;
import com.quectel.util.kit.Snowflake;
import com.quectel.util.mybatis.MyBatisPlusUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.security.SecureRandom;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 *
 * @author ryan
 * @email ryan.ran@quectel.com
 * @date 2023/06/27 15:26
 */
@DubboService
public class PartyEducationTrainingExamServiceImpl implements PartyEducationTrainingExamService{

    private static final Logger LOGGER = LoggerFactory.getLogger(PartyEducationTrainingExamServiceImpl.class);

    /**
     * 入参解释:实体id
     */
    private static final Function<Long, String> PARTY_EDUCATION_TRAINING_EXAM_CACHE_BY_ID_FUNC = id -> String.format(RedisCacheConstants.PROJECT_NAME + "PARTY_EDUCATION_TRAINING_EXAM_CACHE_BY_ID:%d", id);

    /**
     * 清除缓存方法
     */
    private static final Consumer<PartyEducationTrainingExamDto> CLEAR_CACHE_FUNC = dto -> {
        CacheKit.invalidRedisCache(PARTY_EDUCATION_TRAINING_EXAM_CACHE_BY_ID_FUNC.apply(dto.getId()));
    };

    @Autowired
    private PartyEducationTrainingExamDao partyEducationTrainingExamDao;
    @Autowired
    private PartyOrganizationService partyOrganizationService;
    @Autowired
    private TrainingService trainingService;
    @Autowired
    private PartyEducationTrainingExamPersonService partyEducationTrainingExamPersonService;
    @Autowired
    private PartyEducationTrainingExamQuestionService partyEducationTrainingExamQuestionService;
    @Autowired
    private PartyEducationTrainingExamQuestionService examQuestionService;
    @Autowired
    private PartyEducationTrainingExamPersonHistoryService partyEducationTrainingExamPersonHistoryService;

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.TRAINING_EXAM_LOCK + "#{#examSubmitDto.examId}"
            }
    )
    @Transactional
    public void submit(ExamSubmitDto examSubmitDto) {
        PartyEducationTrainingExamPersonHistoryDto partyEducationTrainingExamPersonHistoryDto = partyEducationTrainingExamPersonHistoryService.selectById(examSubmitDto.getTestId());
        Preconditions.checkNotNull(partyEducationTrainingExamPersonHistoryDto);

        PartyEducationTrainingExamDto partyEducationTrainingExamDto = selectById(partyEducationTrainingExamPersonHistoryDto.getExamId());
        Preconditions.checkNotNull(partyEducationTrainingExamDto);

        PartyEducationTrainingExamPersonDto refDto = partyEducationTrainingExamPersonService.selectOne(partyEducationTrainingExamDto.getId(), examSubmitDto.getPersonId());
        Preconditions.checkNotNull(refDto);

        if (PartyEducationExamPersonConstants.TakeStatusEnum.UNEXAMINED.getStatus().equals(refDto.getTakeStatus())) {
            // 该人首次参加该考试，考试计数次数增加
            PartyEducationTrainingExamDto updateObj = new PartyEducationTrainingExamDto();
            updateObj.setId(partyEducationTrainingExamDto.getId());
            updateObj.setExamCompletePersonCount(partyEducationTrainingExamDto.getExamCompletePersonCount() + 1);
            updateById(updateObj);
        }

        // 考题统计相关计数更新
        List<PartyEducationTrainingExamQuestionDto> examQuestionDtos = partyEducationTrainingExamQuestionService.selectByExamId(partyEducationTrainingExamDto.getId());
        Map<Long, PartyEducationTrainingExamQuestionDto> questionMap = new HashMap<>();
        for (PartyEducationTrainingExamQuestionDto examQuestionDto : examQuestionDtos) {
            questionMap.put(examQuestionDto.getQuestionId(), examQuestionDto);
        }
        List<PartyEducationTrainingExamQuestionDto> updateObjList = new ArrayList<>();
        for (PartyEducationQuestionDto question : examSubmitDto.getQuestions()) {
            PartyEducationTrainingExamQuestionDto partyEducationTrainingExamQuestionDto = questionMap.get(question.getId());
            if (partyEducationTrainingExamQuestionDto != null) {
                PartyEducationTrainingExamQuestionDto updateObj = new PartyEducationTrainingExamQuestionDto();
                updateObj.setId(partyEducationTrainingExamQuestionDto.getId());
                updateObj.setCompleteCount(partyEducationTrainingExamQuestionDto.getCompleteCount() == null
                        ? 1 : partyEducationTrainingExamQuestionDto.getCompleteCount() + 1);
                if (PartyEducationQuestionConstants.GradeEnum.RIGHT.getStatus().equals(question.getGrade())) {
                    updateObj.setRightCount(partyEducationTrainingExamQuestionDto.getRightCount() == null
                            ? 1 : partyEducationTrainingExamQuestionDto.getRightCount() + 1);
                }
                updateObjList.add(updateObj);
            }
        }
        partyEducationTrainingExamQuestionService.updateBatch(updateObjList);

        // 更新此次考试的关系
        List<EntrantAnswerCls> originAnswerList = JacksonUtils.parseArray(partyEducationTrainingExamPersonHistoryDto.getAnswer(), EntrantAnswerCls.class);
        Map<Long, EntrantAnswerCls> answerMap = new HashMap<>();
        for (EntrantAnswerCls entrantAnswerCls : originAnswerList) {
            answerMap.put(entrantAnswerCls.getQuestionId(), entrantAnswerCls);
        }

        for (PartyEducationQuestionDto question : examSubmitDto.getQuestions()) {
            EntrantAnswerCls entrantAnswerCls = answerMap.get(question.getId());
            if (Objects.nonNull(entrantAnswerCls)) {
                entrantAnswerCls.setGrade(question.getGrade());
                entrantAnswerCls.setScore(question.getEntrantScore());
                entrantAnswerCls.setEntrantAnswer(Objects.nonNull(question.getEntrantAnswer())
                        ? JacksonUtils.parseArray(question.getEntrantAnswer(), String.class).toArray(new String[0])
                        : new String[]{});
                answerMap.put(entrantAnswerCls.getQuestionId(), entrantAnswerCls);
            }
        }

        PartyEducationTrainingExamPersonHistoryDto updateDto = new PartyEducationTrainingExamPersonHistoryDto();
        updateDto.setId(partyEducationTrainingExamPersonHistoryDto.getId());
        updateDto.setScore(examSubmitDto.getScore());
        updateDto.setAnswer(JacksonUtils.toJsonString(answerMap.values()));
        updateDto.setPassStatus(examSubmitDto.getPassStatus());
        if (!PartyEducationExamPersonConstants.PassStatusEnum.UNRECORDED.getStatus().equals(examSubmitDto.getPassStatus())) {
            // 没有主观题，审阅直接完成
            updateDto.setGradePersonName("系统自动");
            updateDto.setGradeTime(new Date());
        }
        updateDto.setEndTime(examSubmitDto.getSubmitTime());
        partyEducationTrainingExamPersonHistoryService.updateById(updateDto);

        // 考试和人员的关系
        PartyEducationTrainingExamPersonDto updateRef = new PartyEducationTrainingExamPersonDto();
        updateRef.setId(refDto.getId());

        if (!PartyEducationExamPersonConstants.PassStatusEnum.UNRECORDED.getStatus().equals(examSubmitDto.getPassStatus())
                && !PartyEducationExamPersonConstants.PassStatusEnum.PASSED.getStatus().equals(refDto.getPassStatus())) {
            updateRef.setPassStatus(examSubmitDto.getPassStatus());
        }
        if (!PartyEducationExamPersonConstants.PassStatusEnum.UNRECORDED.getStatus().equals(examSubmitDto.getPassStatus())) {
            updateRef.setLastScore(examSubmitDto.getScore());
            if (refDto.getMaxScore() == null || examSubmitDto.getScore().compareTo(refDto.getMaxScore()) > 0) {
                updateRef.setMaxScore(examSubmitDto.getScore());
            }
        }
        updateRef.setTakeCount(refDto.getTakeCount() + 1);
        updateRef.setTakeStatus(PartyEducationExamPersonConstants.TakeStatusEnum.EXAMINED.getStatus());
        updateRef.setLastExamId(partyEducationTrainingExamPersonHistoryDto.getId());
        partyEducationTrainingExamPersonService.updateById(updateRef);
    }

    @Override
    @Transactional
    public ExamTestStartRespDto start(ExamTestStartReqDto startReqDto) {
        final Date currentDate = new Date();

        PartyEducationTrainingExamDto partyEducationTrainingExamDto = selectCacheById(startReqDto.getExamId());
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put(LambdaUtil.getFieldName(PartyEducationTrainingExamQuestionDto::getExamId), partyEducationTrainingExamDto.getId());
        List<QuestionRefExamDto> questionRefExamDtos = examQuestionService.queryQuestionList(queryParams);
        List<QuestionRefExamDto> examQuestions = null;
        if (PartyEducationExamConstants.SelectModeEnum.FIXED.getMode().equals(partyEducationTrainingExamDto.getSelectMode())) {
            examQuestions = questionRefExamDtos;
        } else {
            String questionSetting = partyEducationTrainingExamDto.getQuestionSetting();
            if (StringUtils.isBlank(questionSetting)) {
                // 考试题库设置错误
                return null;
            }
            examQuestions = new ArrayList<>();
            ExamQuestionSettingCls questionSettingCls = JacksonUtils.parse(questionSetting, ExamQuestionSettingCls.class);
            Map<Byte, List<QuestionRefExamDto>> aqm = questionRefExamDtos.parallelStream().collect(Collectors.groupingBy(QuestionRefExamDto::getType));
            SecureRandom secureRandom = new SecureRandom();
            for (PartyEducationQuestionConstants.QuestionTypeEnum value : PartyEducationQuestionConstants.QuestionTypeEnum.values()) {
                String fieldName = CharSequenceUtil.toCamelCase(value.name().toLowerCase());
                ExamQuestionSettingCls.SettingCls settingCls = (ExamQuestionSettingCls.SettingCls) ReflectUtil.getFieldValue(questionSettingCls, fieldName);
                Preconditions.checkNotNull(settingCls);
                if (aqm.get(value.getType()) == null || settingCls.getNumber() > aqm.get(value.getType()).size()) {
                    // 考题设置个数不够
                    return null;
                }
                List<QuestionRefExamDto> aq = aqm.get(value.getType());
                Set<Long> idSet = new HashSet<>();
                while (idSet.size() < settingCls.getNumber()) {
                    QuestionRefExamDto questionRefExamDto = aq.get(secureRandom.nextInt(aq.size()));
                    if (!idSet.contains(questionRefExamDto.getId())) {
                        idSet.add(questionRefExamDto.getId());
                        examQuestions.add(questionRefExamDto);
                    }
                }
            }

        }

        examQuestions.sort(Comparator.comparing(QuestionRefExamDto::getType));
        // 产生题序，移除答案和解析
        for (int i = 1; i <= examQuestions.size(); i++) {
            QuestionRefExamDto questionRefExamDto = examQuestions.get(i - 1);
            questionRefExamDto.setSeq(i);
            questionRefExamDto.setAnswer(null);
            questionRefExamDto.setAnalysis(null);
        }

        List<EntrantAnswerCls> collect = examQuestions.parallelStream().map(o -> {
            EntrantAnswerCls entrantAnswerCls = new EntrantAnswerCls();
            entrantAnswerCls.setSeq(o.getSeq());
            entrantAnswerCls.setQuestionId(o.getId());
            return entrantAnswerCls;
        }).collect(Collectors.toList());

        // 更新首次参加考试的时间
        PartyEducationTrainingExamPersonDto examPersonDto = partyEducationTrainingExamPersonService.selectOne(startReqDto.getExamId(), startReqDto.getPersonId());
        Preconditions.checkNotNull(examPersonDto);
        if (Objects.isNull(examPersonDto.getFirstTime())) {
            PartyEducationTrainingExamPersonDto updateObj = new PartyEducationTrainingExamPersonDto();
            updateObj.setId(examPersonDto.getId());
            updateObj.setFirstTime(new Date());
            partyEducationTrainingExamPersonService.updateById(updateObj);
        }

        // 产生此次考试记录
        PartyEducationTrainingExamPersonHistoryDto newlyDto = new PartyEducationTrainingExamPersonHistoryDto();
        newlyDto.setTenantId(partyEducationTrainingExamDto.getTenantId());
        newlyDto.setPartyId(partyEducationTrainingExamDto.getPartyId());
        newlyDto.setExamId(partyEducationTrainingExamDto.getId());
        newlyDto.setPartyPersonId(startReqDto.getPersonId());
        newlyDto.setAnswer(JacksonUtils.toJsonString(collect));
        newlyDto.setStartTime(currentDate);
        Long save = partyEducationTrainingExamPersonHistoryService.save(newlyDto);

        ExamTestStartRespDto examTestStartRespDto = new ExamTestStartRespDto();
        examTestStartRespDto.setExamQuestions(examQuestions);
        examTestStartRespDto.setTestId(save);
        return examTestStartRespDto;
    }

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.TRAINING_EXAM_LOCK + "#{#updateDto.id}"
            }
    )
    public void updateStatus(PartyEducationTrainingExamDto updateDto) {
        if (updateDto.getId() == null || updateDto.getStatus() == null) {
            return ;
        }

        PartyEducationTrainingExamDto dbExamDto = selectById(updateDto.getId());

        PartyEducationTrainingExamDto newlyDto = new PartyEducationTrainingExamDto();
        newlyDto.setId(dbExamDto.getId());

        if (PartyEducationExamConstants.StatusEnum.UNPUBLISHED.getStatus().equals(dbExamDto.getStatus())
                && PartyEducationExamConstants.StatusEnum.PUBLISHED.getStatus().equals(updateDto.getStatus())) {
            newlyDto.setStatus(PartyEducationExamConstants.StatusEnum.PUBLISHED.getStatus());
        } else if (PartyEducationExamConstants.StatusEnum.PUBLISHED.getStatus().equals(dbExamDto.getStatus())
                && PartyEducationExamConstants.StatusEnum.CANCEL.getStatus().equals(updateDto.getStatus())) {
            newlyDto.setStatus(PartyEducationExamConstants.StatusEnum.CANCEL.getStatus());
        }

        updateById(newlyDto);
    }

    @Override
    public List<PartyEducationTrainingExamDto> selectByTrainingId(Long trainingId) {
        List<PartyEducationTrainingExamEntity> partyEducationTrainingExamEntities = partyEducationTrainingExamDao.selectList(new LambdaQueryWrapper<PartyEducationTrainingExamEntity>()
                .eq(PartyEducationTrainingExamEntity::getTrainingId, trainingId));
        List<PartyEducationTrainingExamDto> partyEducationTrainingExamDtos = CopyUtils.copyList(partyEducationTrainingExamEntities, PartyEducationTrainingExamDto.class);
        partyEducationTrainingExamDtos.forEach(this::paddingField);
        return partyEducationTrainingExamDtos;
    }

    @Override
    public PartyEducationTrainingExamDto selectCacheById(Long id) {
        return CacheKit.cacheToRedis(
                () -> selectById(id),
                PARTY_EDUCATION_TRAINING_EXAM_CACHE_BY_ID_FUNC.apply(id),
                SystemConstants.NOT_NULL_CACHE_EXPIRE_SECONDS
        );
    }

    @Override
    public PartyEducationTrainingExamDto selectById(Long id){
            PartyEducationTrainingExamEntity entity = partyEducationTrainingExamDao.selectById(id);
        return paddingField(CopyUtils.copyObj(entity,PartyEducationTrainingExamDto.class));
    }

    @Override
    public List<PartyEducationTrainingExamDto> queryList(Map<String,Object> params){
        List<PartyEducationTrainingExamEntity> list = partyEducationTrainingExamDao.queryList(params);
        List<PartyEducationTrainingExamDto> result =  CopyUtils.copyList(list,PartyEducationTrainingExamDto.class);
        result.forEach(this::paddingField);
        return result;
    }

    @Override
    public List<PartyEducationTrainingExamDto> queryListWithoutPadding(Map<String,Object> params){
        List<PartyEducationTrainingExamEntity> list = partyEducationTrainingExamDao.queryList(params);
        return CopyUtils.copyList(list,PartyEducationTrainingExamDto.class);
    }

    /**
     * 此方法慎用 禁止填充List一类 比如一个人有多个地址这里不允许填充
     * 填充的原则是: 1:被填充对象不经常变换 2:根据id可以走缓存 3数据足够简单 比如设备类型 所属园区等
     *
     * @param dto
     * @return
     */
    private PartyEducationTrainingExamDto paddingField(PartyEducationTrainingExamDto dto){
        final Date currentDate = new Date();
        if (dto != null){
            PartyOrganizationDto partyOrganizationDto = partyOrganizationService.selectCacheById(dto.getPartyId());
            dto.setParty(partyOrganizationDto);

            TrainingDto trainingDto = trainingService.selectCacheById(dto.getTrainingId());
            dto.setTraining(trainingDto);

            if (PartyEducationExamConstants.StatusEnum.UNPUBLISHED.getStatus().equals(dto.getStatus())) {
                dto.setSpecificStatus(PartyEducationExamConstants.SpecificStatusEnum.UNPUBLISHED.getStatus());
            } else if (PartyEducationExamConstants.StatusEnum.PUBLISHED.getStatus().equals(dto.getStatus())) {
                if (currentDate.getTime() < dto.getStartTime().getTime()) {
                    dto.setSpecificStatus(PartyEducationExamConstants.SpecificStatusEnum.NOT_STARTED.getStatus());
                } else if (currentDate.getTime() > dto.getEndTime().getTime()) {
                    dto.setSpecificStatus(PartyEducationExamConstants.SpecificStatusEnum.ENDING.getStatus());
                } else {
                    dto.setSpecificStatus(PartyEducationExamConstants.SpecificStatusEnum.IN_PROGRESS.getStatus());
                }
            } else if (PartyEducationExamConstants.StatusEnum.CANCEL.getStatus().equals(dto.getStatus())) {
                dto.setSpecificStatus(PartyEducationExamConstants.SpecificStatusEnum.CANCEL.getStatus());
            }
        }
        return dto;
    }

    @Override
    public int queryTotal(Map<String, Object> params){
        return partyEducationTrainingExamDao.queryTotal(params);
    }

    @Override
    @Transactional
    public Long save(PartyEducationTrainingExamDto dto){
        dto.setExamCompletePersonCount(0);
        PartyEducationTrainingExamEntity entity = CopyUtils.copyObj(dto, PartyEducationTrainingExamEntity.class);
        entity.setId(Snowflake.nextId());

        if (dto.getStartTime() != null && dto.getPreTipsMinute() != null) {
            entity.setPushTime(new Date(dto.getStartTime().getTime() - Long.valueOf(dto.getPreTipsMinute()) * 60 * 1000));
        }

        partyEducationTrainingExamDao.insert(entity);

        if (dto.getPersonIds() != null) {
            List<PartyEducationTrainingExamPersonDto> collect = Arrays.stream(dto.getPersonIds()).map(o -> {
                PartyEducationTrainingExamPersonDto partyEducationTrainingExamPersonDto = new PartyEducationTrainingExamPersonDto();
                partyEducationTrainingExamPersonDto.setExamId(entity.getId());
                partyEducationTrainingExamPersonDto.setPartyPersonId(o);
                partyEducationTrainingExamPersonDto.setPartyId(dto.getPartyId());
                partyEducationTrainingExamPersonDto.setTenantId(dto.getTenantId());
                partyEducationTrainingExamPersonDto.setTakeCount(0);
                partyEducationTrainingExamPersonDto.setTakeStatus(PartyEducationExamPersonConstants.TakeStatusEnum.UNEXAMINED.getStatus());
                return partyEducationTrainingExamPersonDto;
            }).collect(Collectors.toList());
            partyEducationTrainingExamPersonService.saveBatch(collect);
        }
        if (dto.getQuestions() != null) {
            ExamQuestionSettingCls parse = JacksonUtils.parse(dto.getQuestionSetting(), ExamQuestionSettingCls.class);

            List<PartyEducationTrainingExamQuestionDto> collect = dto.getQuestions().parallelStream().map(o -> {
                PartyEducationTrainingExamQuestionDto partyEducationTrainingExamQuestionDto = new PartyEducationTrainingExamQuestionDto();
                partyEducationTrainingExamQuestionDto.setTenantId(dto.getTenantId());
                partyEducationTrainingExamQuestionDto.setPartyId(dto.getPartyId());
                partyEducationTrainingExamQuestionDto.setExamId(entity.getId());
                partyEducationTrainingExamQuestionDto.setQuestionId(o.getId());
                partyEducationTrainingExamQuestionDto.setCompleteCount(0);
                partyEducationTrainingExamQuestionDto.setRightCount(0);
                partyEducationTrainingExamQuestionDto.setMissScore(BigDecimal.ZERO);
                if (PartyEducationQuestionConstants.QuestionTypeEnum.RADIO.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getRadio().getScore());
                } else if (PartyEducationQuestionConstants.QuestionTypeEnum.MULTIPLE_CHOICE.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getMultipleChoice().getScore());
                    partyEducationTrainingExamQuestionDto.setMissScore(parse.getMultipleChoice().getMissScore());
                } else if (PartyEducationQuestionConstants.QuestionTypeEnum.DETERMINE.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getDetermine().getScore());
                } else if (PartyEducationQuestionConstants.QuestionTypeEnum.FILL_BLANKS.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getFillBlanks().getScore());
                } else if (PartyEducationQuestionConstants.QuestionTypeEnum.SHORT_ANSWER.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getShortAnswer().getScore());
                }
                return partyEducationTrainingExamQuestionDto;
            }).collect(Collectors.toList());
            partyEducationTrainingExamQuestionService.saveBatch(collect);
        }
        return entity.getId();
    }

    @Override
    @Transactional
    public void updateById(PartyEducationTrainingExamDto dto){
        PartyEducationTrainingExamEntity entity = CopyUtils.copyObj(dto,PartyEducationTrainingExamEntity.class);

        if (dto.getStartTime() != null && dto.getPreTipsMinute() != null) {
            entity.setPushTime(new Date(dto.getStartTime().getTime() - Long.valueOf(dto.getPreTipsMinute()) * 60 * 1000));
        }

        partyEducationTrainingExamDao.updateById(entity);

        if (dto.getPersonIds() != null) {
            partyEducationTrainingExamPersonService.deleteByExamId(entity.getId());
            List<PartyEducationTrainingExamPersonDto> collect = Arrays.stream(dto.getPersonIds()).map(o -> {
                PartyEducationTrainingExamPersonDto partyEducationTrainingExamPersonDto = new PartyEducationTrainingExamPersonDto();
                partyEducationTrainingExamPersonDto.setExamId(entity.getId());
                partyEducationTrainingExamPersonDto.setPartyPersonId(o);
                partyEducationTrainingExamPersonDto.setPartyId(dto.getPartyId());
                partyEducationTrainingExamPersonDto.setTenantId(dto.getTenantId());
                partyEducationTrainingExamPersonDto.setTakeCount(0);
                partyEducationTrainingExamPersonDto.setTakeStatus(PartyEducationExamPersonConstants.TakeStatusEnum.UNEXAMINED.getStatus());
                return partyEducationTrainingExamPersonDto;
            }).collect(Collectors.toList());
            partyEducationTrainingExamPersonService.saveBatch(collect);
        }
        if (dto.getQuestions() != null) {
            ExamQuestionSettingCls parse = JacksonUtils.parse(dto.getQuestionSetting(), ExamQuestionSettingCls.class);

            partyEducationTrainingExamQuestionService.deleteByExamId(entity.getId());
            List<PartyEducationTrainingExamQuestionDto> collect = dto.getQuestions().parallelStream().map(o -> {
                PartyEducationTrainingExamQuestionDto partyEducationTrainingExamQuestionDto = new PartyEducationTrainingExamQuestionDto();
                partyEducationTrainingExamQuestionDto.setTenantId(dto.getTenantId());
                partyEducationTrainingExamQuestionDto.setPartyId(dto.getPartyId());
                partyEducationTrainingExamQuestionDto.setExamId(entity.getId());
                partyEducationTrainingExamQuestionDto.setQuestionId(o.getId());
                partyEducationTrainingExamQuestionDto.setCompleteCount(0);
                partyEducationTrainingExamQuestionDto.setRightCount(0);
                partyEducationTrainingExamQuestionDto.setMissScore(BigDecimal.ZERO);
                if (PartyEducationQuestionConstants.QuestionTypeEnum.RADIO.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getRadio().getScore());
                } else if (PartyEducationQuestionConstants.QuestionTypeEnum.MULTIPLE_CHOICE.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getMultipleChoice().getScore());
                    partyEducationTrainingExamQuestionDto.setMissScore(parse.getMultipleChoice().getMissScore());
                } else if (PartyEducationQuestionConstants.QuestionTypeEnum.DETERMINE.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getDetermine().getScore());
                } else if (PartyEducationQuestionConstants.QuestionTypeEnum.FILL_BLANKS.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getFillBlanks().getScore());
                } else if (PartyEducationQuestionConstants.QuestionTypeEnum.SHORT_ANSWER.getType().equals(o.getType())) {
                    partyEducationTrainingExamQuestionDto.setScore(parse.getShortAnswer().getScore());
                }
                return partyEducationTrainingExamQuestionDto;
            }).collect(Collectors.toList());
            partyEducationTrainingExamQuestionService.saveBatch(collect);
        }

        CLEAR_CACHE_FUNC.accept(dto);
    }

    @Override
    public void updateAllColumnById(PartyEducationTrainingExamDto dto) {

       PartyEducationTrainingExamEntity entity = CopyUtils.copyObj(dto,PartyEducationTrainingExamEntity.class);

        if (dto.getStartTime() != null && dto.getPreTipsMinute() != null) {
            entity.setPushTime(new Date(dto.getStartTime().getTime() - Long.valueOf(dto.getPreTipsMinute()) * 60 * 1000));
        }


        MyBatisPlusUtils.updateAllColumnById(entity, partyEducationTrainingExamDao);

       CLEAR_CACHE_FUNC.accept(dto);
    }

    @Override
    @Transactional
    public void deleteById(Long id){

        CLEAR_CACHE_FUNC.accept(selectById(id));

        partyEducationTrainingExamDao.deleteById(id);
        partyEducationTrainingExamPersonService.deleteByExamId(id);
        partyEducationTrainingExamQuestionService.deleteByExamId(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(Long[] ids){
        for (Long id : ids) {
            deleteById(id);
        }
    }
}
